package com.ld.common.interceptor;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.ld.admin.config.SystemStaticParams;
import com.ld.admin.model.system.SysMenuModel;
import com.ld.admin.model.system.SystemUser;
import com.ld.admin.service.user.IUserService;
import com.ld.admin.service.user.UserService;
import com.ld.admin.util.PermissionUtil;
import com.ld.common.util.LogOutUtil;
import com.ld.common.util.LoginCookieUtil;
import com.ld.common.util.systeminit.SystemInitUtil;
import com.ld.shieldsb.common.composition.util.web.Global;
import com.ld.shieldsb.common.core.model.PropertiesModel;
import com.ld.shieldsb.common.core.model.Result;
import com.ld.shieldsb.common.core.util.SpringContextHolder;
import com.ld.shieldsb.common.core.util.StackTraceUtil;
import com.ld.shieldsb.common.core.util.StringUtils;
import com.ld.shieldsb.common.web.model.SysUser;
import com.ld.shieldsb.common.web.util.Web;

import lombok.extern.slf4j.Slf4j;

/**
 * 登录拦截器
 * 
 * @ClassName SecurityInterceptor
 * @author <a href="mailto:donggongai@126.com" target="_blank">吕凯</a>
 * @date 2018年6月25日 上午8:58:55
 *
 */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    protected String adminPath = Global.CONFIG.getString(Web.Property.CONFIG_ADMINPATH);
    protected String frontPath = Global.CONFIG.getString(Web.Property.CONFIG_FRONTPATH);
    // 使用名称获取，不建议使用类型，名称可以在config配置类中覆盖
    protected IUserService userService = SpringContextHolder.getBeanByClassName(UserService.class);

    // 未登录的处理参数
    public static final String NOLOG_LOGINTYPE = "logintype"; //
    public static final String NOLOG_LOGINTYPE_DIALOG = "dialog"; // ,dialog（弹出框）
    public static final String NOLOG_LOGINTYPE_REDIRECT = "redirect"; // redirect(重定向)
    public static final String NOLOG_LOGINTYPE_RELOAD = "reload"; // reload（当前页面刷新）
    public static final String NOLOG_LOGINURL = "loginurl"; // 跳转url

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        boolean flag = true;
        String uri = Web.Request.getRelativeURI(request); // 相对uri
        if (LogOutUtil.isOut() && LogOutUtil.isOutAction()) { // 总开关、输出action
            log.warn("uri===" + uri);
            if (LogOutUtil.isOutActionTrace()) {
                log.warn("uri:" + uri + " trace===" + StackTraceUtil.getTrace());
            }
        }
        if (SystemInitUtil.needInit() && !isSystemInit(uri)) {
            request.getRequestDispatcher("/toSystemInit").forward(request, response); // 跳转
            return false;
        }
        if (isAdminPath(uri)) { // 后台
            SysUser user = getLoginUser(request);
            if (null == user) { // 用户不存在
                if (loginByCookie(request, response)) { // 通过cookie登录
                    return true;
                }

                if (dealNoLogin(request, response, uri)) { // 未登录处理
                    return true;
                }

                // 重定向
//            ServletContext context = request.getServletContext();
//            String ctxPath = context.getContextPath();
//            response.sendRedirect(ctxPath + adminPath + "/toLogin");
                flag = false;
            } else { // 用户存在，判断是否存在权限
                flag = checkPermission(request);
                if (!flag) {
                    Web.Response.responseJson(response, "无操作权限！", null, false);// 无操作权限
                }

            }

        } else {
            if ("/swagger-ui.html".equals(uri)) { // "v2/api-docs"

            }

        }
        return flag;
    }

    /**
     * 检查是否有权限，有操作权限返回true
     * 
     * @Title checkPermission
     * @author 吕凯
     * @date 2019年8月26日 下午2:08:17
     * @param request
     * @return boolean
     */
    @SuppressWarnings("unchecked")
    protected boolean checkPermission(HttpServletRequest request) {
        boolean flag = true;
        if (PermissionUtil.checkPermission(request)) { // 非顶级管理员，顶级管理员不判断权限
            // 待优化
            Set<String> userPermUrls = (Set<String>) request.getSession().getAttribute(SystemStaticParams.SESSION_USER_PERMS_URL);
            Set<String> userRolenames = (Set<String>) request.getSession().getAttribute(SystemStaticParams.SESSION_USER_ROLENAMES);
            ((SystemUser) Web.Util.getSessionUser(request)).getUserRoles();
            String url = request.getRequestURI();
            // 获取url在权限中的映射，本身url存在则去自己，自己不存在则取上级url对应的权限，
            // 如/system/user/show 先找/system/user/show对应的权限是否存在，存在返回，不存在继续找/system/user对应的权限是否存在，一直找到根路径
            SysMenuModel permModle = PermissionUtil.getPermModleByUrl(url);
            flag = PermissionUtil.checkPerm(request, permModle);
        }
        return flag;
    }

    /**
     * 处理未登录的情况
     * 
     * @Title dealNoLogin
     * @author 吕凯
     * @date 2019年8月26日 下午2:08:35
     * @param request
     * @param response
     * @param uri
     * @return
     * @throws ServletException
     * @throws IOException
     *             boolean
     */
    protected boolean dealNoLogin(HttpServletRequest request, HttpServletResponse response, String uri)
            throws ServletException, IOException {
        String loginurl = PropertiesModel.CONFIG.getString("admin.login.url"); // 后台ajax登录地址，不带项目名及后台名，不拦截
        if (uri.endsWith(loginurl)) { // 登录页不拦截
            return true;
        }
        String requestType = request.getHeader("X-Requested-With");
        if ("XMLHttpRequest".equals(requestType)) { // ajax请求
            Map<String, Object> dataMap = new HashMap<>();
            dataMap.put(NOLOG_LOGINTYPE, NOLOG_LOGINTYPE_DIALOG); // 需要登录：支持 redirect(重定向),reload（当前页面刷新）,dialog（弹出框）
            ServletContext context = request.getServletContext();
            String ctxPath = context.getContextPath();
            if (StringUtils.isNotBlank(loginurl)) {
                dataMap.put(NOLOG_LOGINURL, ctxPath + adminPath + loginurl); // 登录url
            } else {
                if (!NOLOG_LOGINTYPE_RELOAD.equals(dataMap.get(NOLOG_LOGINTYPE))) { // 未定义loginurl的情况应该是刷新，如果类型不是刷新则发出警告
                    log.warn("后台登录地址未配置：admin.login.url，调用reload方法");
                    dataMap.put(NOLOG_LOGINTYPE, NOLOG_LOGINTYPE_RELOAD);
                }
            }
            Web.Response.responseJson(response, "登录超时！", dataMap, false);// 返回登录json
        } else { // 普通页面处理
            request.getRequestDispatcher("/" + adminPath + "/toLogin").forward(request, response); // 跳转
        }
        return false;
    }

    /**
     * 通过cookie登录的处理，登录成功返回true，登录失败返回false
     * 
     * @Title loginByCookie
     * @author 吕凯
     * @date 2019年8月26日 下午2:08:49
     * @param request
     * @param response
     * @return boolean
     */
    protected boolean loginByCookie(HttpServletRequest request, HttpServletResponse response) {
        SystemUser systemUser = LoginCookieUtil.loginCookie2User(request, response); // 判断cookie用户是否存在
        if (systemUser != null) { // cookie存在
            Result result = userService.loginNoCheckVerifyCode(request, systemUser.getLoginCode(), systemUser.getPassword());
            if (result.getSuccess()) {
                return true;
            }
        }
        return false;
    }

    protected SysUser getLoginUser(HttpServletRequest request) {
        SysUser user = Web.Util.getSessionUser(request);
        return user;
    }

    public boolean isAdminPath(String uri) {
        if (!uri.endsWith("/")) {
            uri += "/";
        }
        if (StringUtils.isNotBlank(adminPath)) {
            String pathParam = "/" + adminPath + "/";
            pathParam = pathParam.replaceAll("//+", "/");
            if (StringUtils.isNotBlank(uri) && uri.startsWith(pathParam)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 初始化的页面和请求
     * 
     * @Title isInit
     * @author 吕凯
     * @date 2020年6月4日 下午6:08:11
     * @param uri
     * @return boolean
     */
    public boolean isSystemInit(String uri) {
        if (!uri.endsWith("/")) {
            uri += "/";
        }
        if (uri.equalsIgnoreCase("/toSystemInit/") || uri.equalsIgnoreCase("/systemInit/")) {
            return true;
        }
        return false;
    }

    public boolean isFrontPath(String uri, String adminPath) {
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}